//
//  HQCapture7ViewController.m
//  精通Objective-C
//
//  Created by 花强 on 2017/11/15.
//  Copyright © 2017年 花强. All rights reserved.
//

#import "HQCapture7ViewController.h"
#import "C7Calculator.h"

#import "C3Atom.h"
#import "C3Hydrogen.h"

@interface HQCapture7ViewController ()

@end

@implementation HQCapture7ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    /*
     第7章 运行时系统
     
     终于到了运行时这一章，让我们来一步一步揭开它神秘的面纱吧；
     
     OC拥有相当多的动态特性，这些特性在运行程序时发挥作用，而不是在编译或链接代码时发挥作用；
     OC运行时系统实现了这些特性；这些功能也为OC语言提供了非常强大的功能和灵活性；
        开发人员使用它们能够以实时方式促进程序的开发和更新，而无需重新编译和部署；对软件影响也比较低；
     
     本章内容：
        了解OC运行时系统的工作方式；
        介绍OC语言的动态功能以及在程序中它们的使用方式；
     
     
     7.1 动态功能
     
     在运行时，OC语言会执行其他语言在程序编译或链接时会执行的许多常规操作，如确定类型和方法解析；
        这些操作还可以提供API，使你编写的程序能够执行额外的运行时操作；
        如对象内省和以动态方式创建和加载代码；
        OC运行时系统的结构和实现方式生成了这些动态特性；
     
     接下来介绍这些特性以及在开发程序时使用它们的方式；
     
     
     7.2 对象消息
     
     OOP术语；
     消息传递：指一种在对象之间发送和接收消息的通信方式；
        OC中，消息传递（如对象消息传递）用于调用类和实例（对象）的方法
        语法举例如下：
     接收器            消息
     [加法器   加数1:25 加数2:25]
                        ||
                        选择器
                    加数1:加数2
     
     分析：
        以加法器作为接收器，接收消息（即向加法器发送消息）；
        在上边的这个消息传递表达式中，接收器（加法器）是消息的目的地（即对象或类）；
        消息本身（加数1:25 加数2:25）则是由选择器和相应的输入参数构成；
     
     对象消息传递是以动态方式实现的特性；
        接收器的类型 和 相应的调用方法是在运行时决定的；
        示例如下：
        消息
     [接收器的performComputation方法]
         |
         ——>OC运行时系统：
                1）决定消息接收器的类型（决定的是动态类型，静态类型在声明时就已经决定）
                2）决定实现的方法（动态绑定：关键的是选择器 和 方法签名）
                3）调用方法；（调用接收器的方法-performComputation）
     
     上述过程描述了OC运行时系统通过动态类型和动态绑定，将消息与方法对应起来的方式；
        使用对象消息传递的动态编程特性可以获得极大的灵活性；
        可以简化接口，还可以在执行程序时，开发可修改或更新的模块化应用；
     
     因为OC程序是在运行时处理方法调用的，所以会存在与动态绑定有关的额外开销；
        OC运行时系统通过缓存的方法调用，节省用于向方法发送消息所消耗的内存，从而减少额外的开销；
     
     运行时系统处理方法调用的另一种方式是：
        接收器不保证一定会对消息做出回应；
        如果接收器没有消息做出回应，他就会抛出一个运行时异常；
        OC提供了多个用于处理这种情况的特性（如对象内省和消息转发）；
     
     OC对象消息传递中具有下列关键元素：
     1）消息：向对象/类发送的名称（选择器）和一系列参数；
     2）方法：OC中的类或实例方法，其声明中含有名称、输入参数、返回值和方法签名（即输入参数和返回值的数据类型）；
     3）方法绑定：接收向指定接收器发送的消息并寻找和执行适当方法的处理过程；
        OC运行时在调用方法时，会以动态绑定方式处理消息；
     
     接下来介绍选择器和方法签名（method signature）；
     
     7.2.1 选择器
     
     选择器：在OC的对象消息传递中，选择器是一种字符串，用于指明调用对象或类中的哪个（些）方法；
            选择器是一种分为多个段的文本字符串，每个段以冒号结尾并且后跟参数；
            例如：分段1：分段2：分段3：
            这个选择器有3个分段，每个分段都带有一个冒号，因而表明相应的消息有三个输入参数；
            消息的参数与选择器的分段一一对应；
     
     OC运行时系统使用选择器，为目标对象/类提供正确的方法实现代码；
     在OC源代码中，消息的选择器直接与一个或多个类/实例方法声明对应；
     
     我们举一个例子：
        类A的实例a，定义接口-(int)addNum1:(NSInteger)n1 num2:(NSInteger)n2;
        A类实例方法的选择器为addNum1:num2:
        调用该实例方法需要使用接收器对象a后跟带输入参数的选择器：[a addNum1:1 num2:2];
     
     这个例子展示了选择器在对象消息传递中的作用；
     这些事物的运作方式为：
        当源代码被编译时，编译器（运行时系统的组成部分）会创建数据结构和函数调用语句，使用它们以动态方式将接收器（类/对象）和消息选择器与方法的实现代码对应起来；
        在执行程序时，运行时库（运行时系统的另一个组成部分）利用这些信息找到并调用适当的方法；
        稍后会介绍运行时系统的各个组成部分；
     
     1.空选择器分段：
        举个例子，有这样一个选择器 sumAddend1::
        在这个选择器中，第一个分段具有一个文本字符串，而第二个分段没有文本字符串；
        实际上，拥有一个以上分段的选择器都可以拥有 空选择器分段（即没有文本字符串的分段）；
        空参数（空选择器分段）并不常见，因为出错时会难以对错误进行定位；
     
     2.SEL类型：
        我们已经知道在消息中使用文本字符串定义选择器的方式；接下来看看选择器的类型；
     
     选择器类型（SEL）：是一种特殊的OC数据类型，是用于在编译源代码时替换选择器值的唯一标识符；
        所有具有相同选择器值的方法都拥有相同的SEL标识符；
        OC运行时系统会确保每一个选择器标识符的唯一性；
        可以使用关键字@selector创建SEL类型的变量
     如下示例:
     */
    SEL myMethod = @selector(testSELTypeMethodAddNum1:Num2:);
     /*
      之所以使用SEL类型是因为：
        OC运行时系统（在NSObject中）含有许多将SEL型变量用作参数的动态方法；
        除了获取对象和类的信息，NSObject类还有很多方法，它们用于使用选择器参数调用对象的方法；
      如下示例：
      */
    [self performSelector:@selector(testSELTypeMethodAddNum1:Num2:) withObject:@1 withObject:@2];
    /*
     log：
     2017-11-28 10:33:51.926567+0800 精通Objective-C[641:12164711] a + b = 3

     这个NSObject的实例方法performSelector:withObject:withObject: ，调用了选择器变量指定的方法；
        其中的@selector指令会在编译时创建一个选择器变量；
        也可以使用Foundation框架中的NSSelectorFromString函数在运行时创建选择器；
        因此上述示例你也可以这样写：
     */
    SEL addMethod = NSSelectorFromString(@"testSELTypeMethodAddNum1:Num2:");
    [self performSelector:addMethod withObject:@3 withObject:@2];
    /*
     log：
     2017-11-28 10:37:08.824019+0800 精通Objective-C[717:12171435] a + b = 5
     
     7.2.2 方法签名
     
     前面我们已经介绍了消息接收器、选择器和SEL类型，接下来介绍方法特征及其在对象消息传递中的作用；
     
     方法签名（method signature）：
        定义了方法输入参数的数据类型和方法返回值（如果存在）；
     
     在了解方法签名的作用之前，我们先来研究下运行时系统实现对象消息传递的方式；
     （第八章将详细介绍运行时系统和实现动态行为（如对象消息传递）的方式；本章着重介绍运行时系统的体系结构，而不是细节）
        编译器会将[接收器 消息]形式的对象消息，转换为声明中含有方法签名的（ANSI）C函数调用语句；
        因此，为了生成正确的对象消息传递代码，编译器需要获得选择器值和方法签名；
     
     编译器可以从对象消息表达式中轻松提取选择器；获取方法签名的过程则要麻烦些；
     
     获取方法签名：
        由于接收器（和相应的方法）是在程序运行时确定的，所以编译器无法知道使用怎样的数据类型才能与要调用的方法对应起来;
        为了获取方法签名，编译器会根据已解析的方法声明进行猜测；
        如果找不到方法签名，或者从声明获得的方法签名和运行时实际执行的方法匹配不上，就会出现方法签名不匹配的情况，从而导致从编译器警告到运行时错误的各种问题；
     
     7.2.3 使用对象消息
     进行一个选择器与SEL类型的练习；
     
     我们定义类C7Calculator可以计算整形加法；
        方法实现中使用了NSStringFromSelector(_cmd)获取被调用方法的选择器文本字符串；
        NSStringFromSelector函数需要一个SEL类型的变量；_cmd是一个隐式参数（即无需声明就存在于所有OC方法中的参数），它含有被发送信息中的选择器；
     
     编码测试下：
     */
    C7Calculator * cal = [[C7Calculator alloc]init];
    [cal sumAddend1:@2 addend2:@3];
    /*
     log:
     2017-11-28 15:09:57.628257+0800 精通Objective-C[4697:12909535] Invoking method on C7Calculator object with selector sumAddend1:addend2:

     接下来编写调用对象中动态方法的代码；使用NSObject的performSelector:withObject:withObject:方法，该方法需要制定选择器和参数；
     */
    [cal performSelector:@selector(sumAddend1:addend2:) withObject:@2 withObject:@5];
    [cal performSelector:NSSelectorFromString(@"sumAddend1::") withObject:@1 withObject:@3];
    
    /*
     @selector指令创建的是在编译时；-静态的方式
     NSSelectorFromString函数创建的则是在运行时；-动态的方式
     看起来没什么问题 但是编译器会给出一个警告：PerformSelector may cause a leak because its selector is unknown
        出现这个警告的原因是，如果找不到与该选择器匹配的方法，那么方法就会抛出异常，因而可能导致内存泄漏；
        可以通过添加pragma指令去除这个警告；
     如下述代码：
     */
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    [cal performSelector:NSSelectorFromString(@"sumAddend1::") withObject:@1 withObject:@3];
#pragma clang diagnostic pop
    /*
     使用pragma指令 clang diagnostic ignored "功能诊断的名字" 可以禁止指定的编译器警告功能；
     使用#pragma clang diagnostic push和#pragma clang diagnostic pop，可以保存和恢复编译器当前的诊断设置；
        这可以确保编译器在编译源文件的其余部分时，继续执行正常的编译器选项；
     
     辅助理解：
     运行时包括较多的组成部分，编译器和运行时库都是其中的一部分；一般所说的静态处理是指编译器可判定的，而动态生成/调用则完全是由运行时其他部分决定的；
     OC提供了很多可以在运行时动态调用的方法；
     有@selector创建的SEL类型是静态创建的，但是使用具体实现方法的时候却是在运行时动态调用的；
     
     接下来再看一下运行时系统的其他特性；
     
     
     7.3 动态类型
     
     运行时系统通过动态类型（dynamic type）功能，可以在运行程序时决定对象的类型，因而可以使运行时因素能够在程序中指定使用那些对象；
     在事先无法知道变量分配哪种类型的对象的情况下，就特别有用；
     OC既支持静态类型 也支持动态类型：
        使用静态类型设置变量的类型时，变量的类型就由它的声明决定；
     
     动态类型与静态类型：
        对于静态类型，编译器能在编译时检查类型，执行程序前可以检查出类型错误；
        对于动态类型，类型检查操作是在运行时执行的；
        OC通过id数据类型支持动态类型；（id是OC独有的一种可以存储任何数据类型的OC对象的数据类型）

     动态类型变量的类型放到运行时确定，从而决定对象之间的关系，而不会强制它们使用静态编码；
        这样可以使编写一个可以处理任何类实例的方法变得容易的多，而无需在应用程序中为处理不同类编写多个不同种类的方法；
        使用动态类型可以简化接口；
        使用动态类型还可以提供很大的灵活性，可以在程序的执行过程中改进程序使用的数据类型，并在不重新编译和重新部署的情况下引入新的数据类型；
        在方法中还可以使用不同等级的类型信息，比如使用遵循某协议的对象作为参数，（id<xxxprotocal>）;
     如：
        -(void)testMethod:(id<protocal>)para;
        -(void)testMethod:(NSNumber<protocal> *)para;
     
     OC还为运行时对象的内省提供了API（如检查动态设置类型的匿名对象属于哪个类），解决了部分静态类型检查问题；
        内省使运行时系统能够检查对象的类型，因而能够查明对象是否适用于特定的操作；
     
     我们休息一下，稍后进行后续介绍；
     
     ======================
     
     7.4 动态绑定
     
     动态绑定（dynamic binding）：
        是指在运行程序时（而不是在编译时）将消息与方法对应起来的处理过程；
     
     在运行程序和发送消息前，消息和接收消息的对象不会对应；
        因为许多接收器对象可能会实现相同的方法，调用方法的方式会动态变化；
        也因此，是动态绑定实现了OOP的多态性；
     
     使用动态绑定可以在不影响既有代码的情况下，将新对象和代码 连接或添加到系统中，从而降低对象之前的耦合度；
     动态绑定应用于使用了动态类型的情况；
     
     例如我们有如下的代码：（我们使用第三章的示例类）
     */
    id atom = [[C3Hydrogen alloc]initWithNeutrons:2];
    [atom logInfo];
     /*
     分析：
        在执行这段代码时，运行时系统会确定变量atom的实际类型（通过动态类型：运行时系统会在运行时确定动态类型的实际类型）；
        然后通过动态绑定，使用消息选择器（loginfo）将该消息与接收器（atom对象）的实例方法对应起来；
      
      在本例中：
        变量atom的类型被设置为C3Hydrogen *，因此运行时系统会搜索C3Hydrogen类的实例方法loginfo；
        如果找不到，则会在C3Hydrogen类的父类中继续寻找；
        运行时系统会一直在类层次结构中寻找该实例方法，直到找到为止；
      
      消息[atom loginfo]
            |
            |____>OC运行时系统进行如下处理：
                    1）确定消息接收器类型（动态类型）
                    2）确定实现方法（动态绑定）；注意：无论是动态类型还是动态绑定都是一种功能，可别认为动态类型只是一种类型；
                    3）明确了类型，找到了方法，剩下的就是调用方法了；
      
      动态绑定是OC的一种继承特性：
        不需要任何特定的API；
        使用动态绑定甚至可以将消息选择器设置为在运行程序时确定的变量；
      
      
     7.5 动态方法决议
      
      使用动态方法决议能够以动态方式实现方法；
        使用OC中的@dynamic指令，可以告知编译器与属性关联的方法会以动态的方式实现；
        @dynamic propertyName；//表示编译器须动态地生成该属性对应的方法
        CoreData框架使用@dynamic指令，为管理对象类生成高效属性访问器方法和关系访问器方法；
      
      NSObject类中含有以下两个方法：
        resolveInstanceMethod:
        resolveClassMethod:
        它们能够以动态方式分别为指定的实例和类方法选择器提供实现代码；
        你可以重写这些方法，以动态方式实现实例/类方法；
      
      接下来看看这些方法的使用，来以动态方式为选择器实现方法；
      
      以动态方式实现方法
      
      接下来我们通过更新C7Calculator来以动态方式实现方法，来展示动态方法决议；
        通过重写resolveInstanceMethod:类方法更新类的实现代码；
        C7Calculator.m中的新增代码如下：
      （Code1）
      
      
      我们来分析一下这段代码：
        首先我们需要一条运行时系统库的导入指令（#import <objc/runtime.h>）；这样会将运行时系统的API添加到你编写的代码中；
        这里class_addMethod（）就是一个运行时系统API，使用它能够以动态方式将函数作为实例方法添加到类中；
            class_addMethod([self class],sel,(IMP)absoluteValue,"@@:@");
            该API的输入参数为添加方法的目标类、新方法的选择器、函数的地址和描述方法参数的数据类型的字符串；
            这里，被添加为实例方法的函数为absoluteValue，接收类型为id的输入参数并返回类型id的结果，对应字符串'@@:@'；
            如果是返回值为空，无参数的函数可以表示为‘v@:’这样；
        再看一下实现的函数id absoluteValue(id self , SEL _cmd , id value)
            该函数的输入参数含有隐式参数self和_cmd，self用于设置接受对象，而_cmd用于设置方法的选择器;
      
      我们来实际操作下：
      */
    C7Calculator * calculator = [[C7Calculator alloc]init];
    id sum = [calculator performSelector:NSSelectorFromString(@"absoluteValue") withObject:@-2];
    NSLog(@"%@",sum);
    /*
     log：
     2017-12-05 16:29:58.508652+0800 精通Objective-C[56598:18002143] Dynamic added instance method absoluteValue to class C7Calculator
     2017-12-05 16:29:58.508763+0800 精通Objective-C[56598:18002143] para object class:C7Calculator
     para method SEL:absoluteValue
     2017-12-05 16:29:58.508851+0800 精通Objective-C[56598:18002143] 2
     
     来看看这段代码：
        这段代码以动态方式为新建方法创建了一个选择器：NSSelectorFromString(@"absoluteValue")
        然后，使用该选择器调用了一个实例方法，通过动态方法决议可以在运行程序时 添加和调用 这个方法，并获得他的返回值；
        当使用选择器的消息通过performSelector:withObject:方法被以动态方式调用时，OC运行时系统会将新方法添加到C7Calculator类中；
     
     可以看到动态方法决议可用于在运行程序时向类中添加方法；
     
     
     7.6 动态加载
     
     OC程序通过 动态加载 功能可以根据需要加载可执行代码和源代码，而无需在启动程序时就加载程序的所有组件；
     
     延迟加载（lazy-loading）：
        可执行代码（在加载前就链接好的）可以含有新的类，并使这些新的类在运行程序时整合到整个程序中；
        这种程序代码和数据资源的延迟加载方式可以提高程序的整体性能，因为它降低了系统内存的需求；
        该方式还提高了程序的可扩展性，因为能使新软件在不更改已存在程序的情况下，以动态方式将新增代码添加到程序中；
        比如以动态方式加载软件的包bundle机制；
     
     包是一种软件的交付机制；
        包由具有标准层次结构的目录以及该目录中的可执行代码和源代码构成；
        包可以包含可执行代码、图像、音频文件和其他类型的代码以及资源数据；
        还含有一个运行时配置文件，即信息属性列表Info.plist；
     
     包定义了组织与软件有关的代码和数据资源的基本结构，可分为一下几种类型：
     1）应用程序包：
        应用程序包 管理与进程（程序）有关的代码和数据资源；
     2）框架包：
        框架包 管理以动态方式共享的软件库及相关的数据资源（如头文件）；
        应用程序可以连接一个或多个框架，如Foundation框架；
     3）可选加载包：
        可选加载包（也称插件）是用于以动态方式加载自定义代码的包；
     
     可以使用Foundation框架中的NSBundle类管理 包；
        一个NSBundle对象就代表文件系统中的一个存储位置，该位置存储着可在程序中使用的代码和数据资源；
        比如我们可以这样加载当前应用程序的信息属性列表（Info.plist）的路径；
     */
    NSBundle * bundle = [NSBundle mainBundle];
    NSString * bundlePath = [bundle pathForResource:@"Info" ofType:@"plist"];
    NSLog(@"Info plist path:%@",bundlePath);
    /*
     log:
     2017-12-05 17:46:36.815789+0800 精通Objective-C[57256:18059881] Info plist path:/Users/huaqiang/Library/Developer/CoreSimulator/Devices/3FB29963-72AE-4642-A3E0-5732A40C6267/data/Containers/Bundle/Application/2F0A7F42-1DDA-4B0E-A6B1-DAE8C78157C1/精通Objective-C.app/Info.plist
     
     前面说到了使用包能够动态加载可执行代码，你可以这样做：
        使用一个NSBundle对象，以动态的方式加载了一个框架包，再通过该框架创建一个实例；
        NSBundle * testBundle = [NSBundel bundleWithPath:@"/Test.bundle"];
        id tester = [[[testBundle classNamed:@"Tester"] alloc] init];
     
     
     7.7 内省
     
     前面我们提到“如检查动态设置类型的匿名对象属于哪个类”用的就是自省；
     Foundation框架中的NSObject类的API含有非常多用于执行对象内省的方法；
     
     使用这些方法能够以动态方式在程序运行时查询下列信息：
     1）与方法有关的信息；
     2）测试对象的继承性、行为和一致性的信息；
     
     因为OC运行时行为与编译连接时行为差异大，所以对象内省就成了一种关键功能；
        使用它可以避免运行时错误，如消息分派错误、对对象相等的错误假设以及其他问题；
     
     [object isKindOfClass：[ClassA class]];
        这个NSObject的实例方法可以用来测试接收器是ClassA类的实例还是其子类的实例；
     
     [object respondsToSelector：@selector（selector）];
        这个方法可以检查某个对象是否会对选择器做出回应，即该对象是否实现了或继承了能够对指定消息做出回应的方法；
     
     [object conformsToProtocol：@protocol(protocol)];
        检查对象是否遵循了指定的协议；
     
     我们可以这样为选择器提取方法签名：
     */
    NSMethodSignature * signature = [atom methodSignatureForSelector:@selector(massNumber)];
    
    NSLog(@"signature:%ld",[signature numberOfArguments]);
    /*
     log:
     2017-12-05 18:08:40.597009+0800 精通Objective-C[57482:18078532] @
     2017-12-05 18:08:40.597102+0800 精通Objective-C[57482:18078532] :
     2017-12-05 18:08:40.597200+0800 精通Objective-C[57482:18078532] returnType:Q ,returnLen:8
     2017-12-05 18:08:40.597329+0800 精通Objective-C[57482:18078532] signature:<NSMethodSignature: 0x60000026a840>
     
     2017-12-05 18:08:40.597446+0800 精通Objective-C[57482:18078532] signature:2
     
     这些都是使用NSObject类的方法进行对象内省的简单示例，后续我们还会介绍NSObject类的API；
     
     
     7.8 小结
     
     本章介绍了OC运行时系统的特性和关键组件；
     本章要点：
     1）使用OC中的消息传递（对象消息传递）特性何以调用类和对象中的方法；
        对象消息传递是一种动态特性，及接收器和接收器中的方法是在运行程序时确定的；
     2）消息传递表达式包含接收器（接收消息的对象/类）和消息，而消息又由选择器和相应的输入参数构成；
     3）选择器是一种分段的文本字符串，每个分段以冒号结尾并且后跟参数；
        含有一个以上分段的选择器可以拥有空选择器分段（即不带名称的参数）；
     4）选择器数据类型（SEL）是一种特殊的OC数据类型，它用于在编译源代码时使用具有唯一性的标识符替换选择器；
        使用@selector关键字或Foundation框架中的NSSelectorFromString()函数，可以创建类型为SEL的变量；
     5）方法签名定义了方法的输入参数和返回值的数据类型；
        方法签名不匹配是指编译器无法为对象消息确定适当的方法，或者方法声明与运行时实际执行的方法不匹配；
     6）使用动态类型功能可以在运行程序时决定对象的类型，能够由运行时因素决定在程序中使用哪种类型的对象；
        OC通过id数据类型支持动态类型；
     7）动态绑定指在程序运行时（不是编译时）将消息与方法对应起来的处理过程；
        动态绑定实现了OOP的多态性；
        可以在不影响已有代码的情况下，将新代码和对象添加到程序中，降低耦合度；
     8）使用动态方法决议能够以动态的方式实现方法；
        使用OC的@dynamic指令可以告知编译器与某个属性关联的方法会以动态方式实现；
        可以使用NSObject实例方法resolveInstanceMethod: 、resolveClassMethod:以动态方式分别实现由选择器指定的实例和类方法；
     9）使用动态加载功能可以根据需要加载OC程序的可执行代码和源代码，而无需在启动应用程序时加载它的所有组件；
        系统提供的包机制，该机制支持在平台上以动态的方式加载软件；
        可以使用NSBundle类管理包
     10）Foundation框架提供了很对执行对象内省的方法；
        运行程序时，这些API能够以动态的方式查询方法的信息；
        他们还可以测试对象的继承性、行为和一致性；
     
     本章介绍了运行时系统的特性和在程序汇总使用的方式；下一章介绍运行时系统的结构和实现方式；
     
     
     推荐大家读一下《Objecttice-C 2.0运行时系统编程指南》我之前读过，眼下重新读的话，估计能收获会更多些；
     
     */
}
-(NSNumber *)testSELTypeMethodAddNum1:(NSNumber *)a Num2:(NSNumber *)b{
    NSNumber * c = [NSNumber numberWithFloat: [a floatValue] + [b floatValue]];
    NSLog(@"a + b = %@",c);
    return c;
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end
